package ch.subgate.spritestudio;

import java.util.ArrayList;

import ch.subgate.spritestudio.R;
import ch.subgate.spritestudio.constants.Constants;
import ch.subgate.spritestudio.tools.DataManager;
import ch.subgate.spritestudio.tools.DialogHelper;
import ch.subgate.spritestudio.tools.OnColorChangedListener;
import ch.subgate.spritestudio.types.PaintEvent;
import ch.subgate.spritestudio.types.Sprite;
import ch.subgate.spritestudio.widgets.ColorChooserButton;
import ch.subgate.spritestudio.widgets.PainterView;
import ch.subgate.spritestudio.widgets.PainterView.PainterThread;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.Toast;

public class PainterActivity extends Activity implements
OnTouchListener,
OnClickListener,
OnColorChangedListener {
	
	/** Variable to hold a handle to the PixelPaintView */
    private PainterView mView;
    
    /** Variable to hold a handle to the PixelPaintThread */
    private PainterThread mThread;
    
    /** Variable holds the currently selected tool */
    private int mTool;
    
    /** Variable holds the currently used color */
    private int mColor;
    
    /** Variable stores the x coordinate when the display is touched */
    private float mTouchDownXa;
    
    /** Variable stores the y coordinate when the display is touched */
    private float mTouchDownYa;
    
    /** Stores the x coordinate of a second touch event */
    private float mTouchDownXb;
    
    /** Stores the y coordinate of a second touch event */
    private float mTouchDownYb;
    
    /** Variable stores the x coordinate when first touch spot moves */
    private float mTouchMoveXa;
    
    /** Variable stores the y coordinate when first touch spot moves */
    private float mTouchMoveYa;
    
    /** Variable stores the x coordinate when second touch spot moves */
    private float mTouchMoveXb;
    
    /** Variable stores the y coordinate when second touch spot moves */
    private float mTouchMoveYb;
    
    /** Stores wether there are currently multiple touches going on */
    private boolean mMultitouch;
    
    /** Variable stores an ArrayList where the changes to a picture are logged */
    private ArrayList<PaintEvent> mPaintEvents;
    
    /** Variable stores current sprite */
    private Sprite mSprite;
    
    private PaintEvent mCurrentPaintEvent;
    
    /*
     * Handles to all the interface buttons.
     */
    private Button mPencilButton;
    private Button mEraserButton;
    private Button mBucketButton;
    private Button mMoveButton;
    private Button mColorpickerButton;
    private ColorChooserButton mCurrentColor;
    private Button mPaletteButton;
    private Button mUndoButton;
    private Button mRedoButton;
	
	/** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.painter_layout);
        
        mSprite = null;
        
        //Set the handles for the view, the thread and the picture
        mView = (PainterView) findViewById(R.id.spritestudio);
        mThread = mView.getThread();
        if (savedInstanceState != null) {
        	//Get the currently used tool from the bundle
            mTool = savedInstanceState.getInt("currentTool", Constants.TOOL_PENCIL);
            
            //Get the currently used color from the bundle
            mColor = savedInstanceState.getInt("currentColor", Color.BLACK);
            
            //Set the picture attributes as saved
            mSprite = new Sprite(
            		savedInstanceState.getString("spritename"),
            		savedInstanceState.getString("projectname"));
            mSprite.setBitmap(
            		savedInstanceState.getInt("pictureWidth", 0),
            		savedInstanceState.getInt("pictureHeight", 0),
            		savedInstanceState.getIntArray("pixels"),
            		savedInstanceState.getInt("pictureFrames", 1));
            mThread.setSprite(mSprite);
            mThread.setView(
            		savedInstanceState.getFloat("viewX", 0),
            		savedInstanceState.getFloat("viewY", 0));
        }
        else {
        	//Set the current tool to pencil
        	mTool = Constants.TOOL_PENCIL;
        	
        	//Set the used color to black 0xff000000
        	mColor = Color.BLACK;
        	
        	Intent intent = getIntent();
            Bitmap bitmap = null;
            if (intent != null) {
            	if (intent.getBooleanExtra("newfile", true)) {
            		bitmap = Bitmap.createBitmap(
            				intent.getIntExtra("width", Constants.DEFAULT_SPRITE_SIZE),
            				intent.getIntExtra("height", Constants.DEFAULT_SPRITE_SIZE),
            				Bitmap.Config.ARGB_8888);
            		bitmap.eraseColor(Color.TRANSPARENT);
            		mSprite = new Sprite(
            				intent.getStringExtra("filename"),
            				intent.getStringExtra("projectname"));
            		mSprite.setBitmap(bitmap, 1);
            		mThread.setSprite(mSprite);
            	}
            	else {
            		//Load sprite from sdcard according to intent data
                	String fileName = intent.getStringExtra("filename");
                	String projectName = intent.getStringExtra("projectname");
                	
                	bitmap = DataManager.loadBitmapFromNames(fileName, projectName);
                	if (bitmap != null) {
                		mSprite = new Sprite(
                				fileName,
                				projectName);
                		mSprite.setBitmap(bitmap, 1); // TODO get frames from database
                		mThread.setSprite(mSprite);
                	}
                	else {
                		Toast.makeText(
                				this,
                				"Error: Unable to load sprite: " + fileName + " from " + projectName,
                				Toast.LENGTH_LONG).show();
                		//Exit activity
                		finish();
                	}
            	}
            }
            else {
            	finish();
            }
        }
        
        //Get all the button views and store a reference
        mPencilButton = (Button) findViewById(R.id.pencil);
        mEraserButton = (Button) findViewById(R.id.eraser);
        mBucketButton = (Button) findViewById(R.id.bucket);
        mMoveButton = (Button) findViewById(R.id.move);
        mColorpickerButton = (Button) findViewById(R.id.colorpicker);
        mPaletteButton = (Button) findViewById(R.id.palette);
        mUndoButton = (Button) findViewById(R.id.undo);
        mRedoButton = (Button) findViewById(R.id.redo);
        
        //Get the reference to the current color ImageView
        mCurrentColor = (ColorChooserButton) findViewById(R.id.currentColor);
        
        //Set the listeners for the buttons
        mPencilButton.setOnClickListener(this);
        mEraserButton.setOnClickListener(this);
        mBucketButton.setOnClickListener(this);
        mMoveButton.setOnClickListener(this);
        mColorpickerButton.setOnClickListener(this);
        mCurrentColor.setOnClickListener(this);
        mPaletteButton.setOnClickListener(this);
        mUndoButton.setOnClickListener(this);
        mRedoButton.setOnClickListener(this);
        
        //Set touchListener for SurfaceView
        mView.setOnTouchListener(this);
        
        //Set touch coordinates to default;
        mTouchDownXa = 0.0f;
        mTouchDownYa = 0.0f;
        mTouchDownXb = 0.0f;
        mTouchDownYb = 0.0f;
        
        //Initialize touch states
        mMultitouch = false;
        
        //Initialize paintEvents ArrayList
        mPaintEvents = new ArrayList<PaintEvent>();
        
        updateUIButtons(0, mTool);
    }
    
    /** Called when the activity is paused */
    @Override
    public void onPause() {
    	super.onPause();
    }
    
    /** Called when the activity comes back to the front */
    @Override
    public void onResume() {
    	super.onResume();
    }
    
    /** Called when the activity is finally destroyed */
    @Override
    public void onDestroy() {
    	//Finally stopping the thread
    	mThread.setRunning(false);
    	mThread.freeData();
		
		while (mThread != null) {
			try {
				mThread.join();
				mThread = null;
			} catch (InterruptedException e) {
				
			}
		}
		
		mCurrentPaintEvent = null;
		for (PaintEvent event : mPaintEvents) {
			event.recycle();
			event = null;
		}
		mPaintEvents.clear();
		mPaintEvents = null;
		mSprite.recycle();
		mSprite = null;
		mPencilButton = null;
	    mEraserButton = null;
	    mBucketButton = null;
	    mMoveButton = null;
	    mColorpickerButton = null;
	    mCurrentColor = null;
	    mPaletteButton = null;
	    mUndoButton = null;
	    mRedoButton = null;
		
		super.onDestroy();
    }
    
    /** Called when options menu is created */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
    	MenuInflater inflater = getMenuInflater();
    	inflater.inflate(R.menu.paintview_menu, menu);
    	return true;
    }
    
    /** Called when an options menu item is selected */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
    	switch (item.getItemId()) {
    		case R.id.paintview_save:
    			DataManager.saveSpritePNG(this, mSprite);
    			break;
    	}
    	return true;
    }
    
    /** Called upon touch events */
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		PaintEvent result;
		switch (event.getAction() & MotionEvent.ACTION_MASK) {
			case MotionEvent.ACTION_DOWN:
				mTouchDownXa = event.getX(0);
				mTouchDownYa = event.getY(0);
				result = mThread.performAction(
						mTouchDownXa,
						mTouchDownYa,
						mColor,
						null,
						mTool);
				if (result != null) {
					if (mPaintEvents.size() >= Constants.MAX_UNDO_EVENTS) {
						mPaintEvents.get(0).recycle();
						mPaintEvents.remove(0);
					}
					mPaintEvents.add(result);
					mCurrentPaintEvent = result;
				}
				
				if (mTool == Constants.TOOL_PIPETTE) {
					mColor = mThread.getTouchedPixelColor(
							mTouchDownXa,
							mTouchDownYa);
					mCurrentColor.setCurrentColor(mColor);
				}
				break;
			case MotionEvent.ACTION_POINTER_DOWN:
				updateUIButtons(mTool, Constants.TOOL_POSITION);
				mTool = Constants.TOOL_POSITION;
				mMultitouch = true;
				mTouchDownXa = event.getX(0);
				mTouchDownYa = event.getY(0);
				mTouchDownXb = event.getX(1);
				mTouchDownYb = event.getY(1);
				break;
			case MotionEvent.ACTION_MOVE:
				result = mThread.performAction(
						event.getX(0),
						event.getY(0),
						mColor,
						mCurrentPaintEvent,
						mTool);
				if (result != null && mCurrentPaintEvent == null) {
					if (mPaintEvents.size() >= Constants.MAX_UNDO_EVENTS) {
						mPaintEvents.get(0).recycle();
						mPaintEvents.remove(0);
					}
					mPaintEvents.add(result);
					mCurrentPaintEvent = result;
				}
				if (mTool == Constants.TOOL_POSITION) {
					if (mMultitouch) {
						mTouchMoveXa = event.getX(0);
						mTouchMoveYa = event.getY(0);
						mTouchMoveXb = event.getX(1);
						mTouchMoveYb = event.getY(1);
						mThread.doZoom(
								mTouchDownXa, mTouchDownYa,
								mTouchDownXb, mTouchDownYb,
								mTouchMoveXa, mTouchMoveYa,
								mTouchMoveXb, mTouchMoveYb);
					}
					else {
						if (
								Math.abs(event.getX(0) - mTouchDownXa)
								+ Math.abs(event.getY(0) - mTouchDownYa) > 10.0f) {
							mThread.doMove(
									event.getX(0) - mTouchDownXa,
									event.getY(0) - mTouchDownYa);
						}
					}
				}
				break;
			case MotionEvent.ACTION_POINTER_UP:
				mMultitouch = false;
				int pid = event.getAction() >> MotionEvent.ACTION_POINTER_ID_SHIFT;
				if (pid == 0) {
					// Second finger becomes primary finger
					mTouchDownXa = mTouchMoveXb;
					mTouchDownYa = mTouchMoveYb;
				}
				break;
			case MotionEvent.ACTION_UP:
				if (mMultitouch) {
					mTouchDownXa = event.getX(0);
					mTouchDownYa = event.getY(0);
				}
				mMultitouch = false;
				switch (mTool) {
					case Constants.TOOL_POSITION:
						mThread.doUpdate();
						break;
				}
				updateUIButtons(0, 0);
				break;
		}
		return true;
	}
	
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		switch (keyCode) {
		case KeyEvent.KEYCODE_BACK:
			AlertDialog.Builder builder = new AlertDialog.Builder(this);
			builder.setMessage("Save before leaving?");
			builder.setCancelable(false);
			builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
				
				@Override
				public void onClick(DialogInterface dialog, int which) {
					// Save before leaving
					DataManager.saveSpritePNG(
							PainterActivity.this,
							PainterActivity.this.mSprite);
					Intent data = new Intent();
					data.putExtra("filepath", PainterActivity.this.mSprite.getSpritePath());
					data.putExtra("fileupdated", true);
					PainterActivity.this.setResult(RESULT_OK, data);
					PainterActivity.this.finish();
				}
			});
			builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
				
				@Override
				public void onClick(DialogInterface dialog, int which) {
					// Finish without saving
					Intent data = new Intent();
					data.putExtra("filepath", PainterActivity.this.mSprite.getSpritePath());
					data.putExtra("fileupdated", false);
					PainterActivity.this.setResult(RESULT_OK, data);
					PainterActivity.this.finish();
				}
			});
			AlertDialog dialog = builder.create();
			dialog.show();
			return true;
		default:
			return false;
		}
	}
	
	/** Called before activity is destroyed */
	@Override
	public void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		
		int pictureWidth = mSprite.mTotalWidth;
		int pictureHeight = mSprite.mHeight;
		int[] pixels = new int[pictureWidth * pictureHeight];
		mSprite.getBitmap().getPixels(pixels, 0, pictureWidth, 0, 0, pictureWidth, pictureHeight);
		
		//Saving the bitmap, its size, the currently selected color and tool
		outState.putIntArray("pixels", pixels);
		outState.putInt("pictureWidth", pictureWidth);
		outState.putInt("pictureHeight", pictureHeight);
		outState.putInt("pictureFrames", mSprite.mFrames);
		outState.putInt("currentColor", mColor);
		outState.putInt("currentTool", mTool);
		outState.putFloat("viewX", mThread.getViewX());
		outState.putFloat("viewY", mThread.getViewY());
		outState.putString("spritename", mSprite.mFileName);
		outState.putString("projectname", mSprite.mProjectName);
	}
	
	/** Called when activity is restored */
	@Override
	public void onRestoreInstanceState(Bundle savedInstanceState) {
		super.onRestoreInstanceState(savedInstanceState);
		
		//Set the current tool to pencil
        mTool = savedInstanceState.getInt("currentTool", Constants.TOOL_PENCIL);
        
        //Set the used color to black 0xff000000
        mColor = savedInstanceState.getInt("currentColor", Color.BLACK);
        
        //Set the picture attributes as saved
        mSprite = new Sprite(
        		savedInstanceState.getString("spritename"),
        		savedInstanceState.getString("projectname"));
        mSprite.setBitmap(
        		savedInstanceState.getInt("pictureWidth", 0),
        		savedInstanceState.getInt("pictureHeight", 0),
        		savedInstanceState.getIntArray("pixels"),
        		savedInstanceState.getInt("pictureFrames", 1));
        mThread.setSprite(mSprite);
        mThread.setView(
        		savedInstanceState.getFloat("viewX", 0),
        		savedInstanceState.getFloat("viewY", 0));
	}
	
	/** Called upon clicks */
	@Override
	public void onClick(View v) {
		switch (v.getId()) {
			case R.id.pencil:
				updateUIButtons(mTool, Constants.TOOL_PENCIL);
				mTool = Constants.TOOL_PENCIL;
				Toast.makeText(getApplicationContext(), "Pencil", Toast.LENGTH_SHORT).show();
				break;
			case R.id.eraser:
				updateUIButtons(mTool, Constants.TOOL_ERASER);
				mTool = Constants.TOOL_ERASER;
				Toast.makeText(getApplicationContext(), "Eraser", Toast.LENGTH_SHORT).show();
				break;
			case R.id.bucket:
				updateUIButtons(mTool, Constants.TOOL_PAINTBUCKET);
				mTool = Constants.TOOL_PAINTBUCKET;
				Toast.makeText(getApplicationContext(), "Paintbucket", Toast.LENGTH_SHORT).show();
				break;
			case R.id.move:
				updateUIButtons(mTool, Constants.TOOL_POSITION);
				mTool = Constants.TOOL_POSITION;
				Toast.makeText(getApplicationContext(), "Positioner", Toast.LENGTH_SHORT).show();
				break;
			case R.id.colorpicker:
				updateUIButtons(mTool, Constants.TOOL_PIPETTE);
				mTool = Constants.TOOL_PIPETTE;
				Toast.makeText(getApplicationContext(), "Eyedropper", Toast.LENGTH_SHORT).show();
				break;
			case R.id.undo:
				PaintEvent undoevent;
				if (!mPaintEvents.isEmpty()) {
					for (int i = mPaintEvents.size() - 1; i >= 0; i--) {
						undoevent = mPaintEvents.get(i);
						if (undoevent.getApplied()) {
							undoevent.undoEvent(mSprite.getBitmap());
							undoevent.setApplied(false);
							break;
						}
					}
				}
				updateUIButtons(0, 0);
				Toast.makeText(getApplicationContext(), "Undo", Toast.LENGTH_SHORT).show();
				break;
			case R.id.redo:
				PaintEvent redoevent;
				if (!mPaintEvents.isEmpty()) {
					for (int i = 0; i < mPaintEvents.size(); i++) {
						redoevent = mPaintEvents.get(i);
						if (!redoevent.getApplied()) {
							redoevent.doEvent(mSprite.getBitmap());
							redoevent.setApplied(true);
							break;
						}
					}
				}
				updateUIButtons(0, 0);
				Toast.makeText(getApplicationContext(), "Redo", Toast.LENGTH_SHORT).show();
				break;
			case R.id.currentColor:
				DialogHelper.showColorPickerDialog(
						this,
						this,
						mColor);
				break;
			case R.id.palette:
				// Start palette activity
				Intent intent = new Intent(
						PainterActivity.this,
						PaletteActivity.class);
				intent.putExtra("currentcolor", mColor);
				intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
				startActivityForResult(intent, 0);
				break;
		}
	}
	
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (resultCode == RESULT_OK) {
			mColor = data.getIntExtra("resultcolor", 0xff000000);
			mCurrentColor.setCurrentColor(mColor);
		}
		else {
			Toast.makeText(
					getApplicationContext(),
					"Unable to receive result color.",
					Toast.LENGTH_SHORT).show();
		}
	}
	
	/** Called when a button is clicked, sets the according button states */
	public void updateUIButtons(int oldTool, int newTool) {
		// No need to change anything if old tool equals new tool
		if (oldTool != newTool && newTool != 0) {
			// Set the old tool button to inactive
			switch (oldTool) {
				case Constants.TOOL_PENCIL:
					mPencilButton.setBackgroundResource(R.drawable.pencil_button_d);
					break;
				case Constants.TOOL_POSITION:
					mMoveButton.setBackgroundResource(R.drawable.move_button_d);
					break;
				case Constants.TOOL_PIPETTE:
					mColorpickerButton.setBackgroundResource(R.drawable.colorpicker_button_d);
					break;
				case Constants.TOOL_ERASER:
					mEraserButton.setBackgroundResource(R.drawable.eraser_button_d);
					break;
				case Constants.TOOL_PAINTBUCKET:
					mBucketButton.setBackgroundResource(R.drawable.bucket_button_d);
					break;
			}
			
			// Set the new tool button to active
			switch (newTool) {
				case Constants.TOOL_PENCIL:
					mPencilButton.setBackgroundResource(R.drawable.pencil_button_a);
					break;
				case Constants.TOOL_POSITION:
					mMoveButton.setBackgroundResource(R.drawable.move_button_a);
					break;
				case Constants.TOOL_PIPETTE:
					mColorpickerButton.setBackgroundResource(R.drawable.colorpicker_button_a);
					break;
				case Constants.TOOL_ERASER:
					mEraserButton.setBackgroundResource(R.drawable.eraser_button_a);
					break;
				case Constants.TOOL_PAINTBUCKET:
					mBucketButton.setBackgroundResource(R.drawable.bucket_button_a);
			}
		}
		
		boolean hasDone = false;
		boolean hasUndone = false;
		if (!mPaintEvents.isEmpty()) {
			for (PaintEvent pe : mPaintEvents) {
				if (pe.getApplied()) {
					hasDone = true;
				}
				else {
					hasUndone = true;
				}
			}
			
			if (hasDone) {
				mUndoButton.setBackgroundResource(R.drawable.undo_button_a);
			}
			else {
				mUndoButton.setBackgroundResource(R.drawable.undo_button_d);
			}
			
			if (hasUndone) {
				mRedoButton.setBackgroundResource(R.drawable.redo_button_a);
			}
			else {
				mRedoButton.setBackgroundResource(R.drawable.redo_button_d);
			}
		}
		else {
			mUndoButton.setBackgroundResource(R.drawable.undo_button_d);
			mRedoButton.setBackgroundResource(R.drawable.redo_button_d);
		}
	}

	@Override
	public void onColorChanged(int color) {
		mColor = color;
		mCurrentColor.setCurrentColor(color);
	}
}